package worker

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"strings"
	"text/template"
	"time"

	"github.com/hedzr/cmdr/v2/cli"
	"github.com/hedzr/cmdr/v2/internal/tool"
	"github.com/hedzr/cmdr/v2/pkg/logz"
	"github.com/hedzr/is/exec"
	"github.com/hedzr/is/term/color"
)

type (
	manPainter struct {
		writer io.Writer
		color.Translator
		// buffer bufio.Writer
	}
)

func (s *manPainter) printHeader(ctx context.Context, sb *strings.Builder, cc cli.Cmd, pc cli.ParsedState, cols, tabbedW int) {
	root := cc.Root()
	a := &manHdrData{
		root,
		root.AppLongDescription(),
		time.Now().Format("Jan 2006"),
		manExamples(root.Examples(), root),
	}

	s.bufPrintf(sb, "%v", tplApply(`
.pc
.nh
.TH {{.AppName}} 1 "{{.TimeMY}}" "{{.Version}}" "Tool with cmdr"
Auto generated by hedzr/cmdr

.SH NAME
.PP
{{.AppName}} v{{.Version}} - {{.Copyright}}

`, a))

	if cc.OwnerIsNil() {
		s.bufPrintf(sb, "%v", tplApply(`

.SH SYNOPSIS
.PP
\fB{{.AppName}} generate manual [flags]\fP

.SH DESCRIPTIONS
.PP
{{.LongDesc}}

.SH EXAMPLES

{{.ManExamples}}

.\" .SH TIPS
.\" 
.\" 	NAME, SYNOPSIS, CONFIGURATION, DESCRIPTION, OPTIONS, EXIT STATUS, RETURN VALUE, ERRORS, 
.\" 	ENVIRONMENT, FILES, VERSIONS, CONFORMING TO, NOTES, BUGS, EXAMPLE, AUTHORS, and SEE ALSO.

`, a))
	}
}

func getph1st(c cli.Cmd) string {
	if s := c.TailPlaceHolder(); s != "" {
		return s
	} else if len(s) > 0 {
		return s
	}
	return ""
}

func getphtail(c cli.Cmd) string {
	if s := c.TailPlaceHolder(); len(s) > 0 {
		return s[1:]
	}
	return ""
}

func (s *manPainter) printUsage(ctx context.Context, sb *strings.Builder, cc cli.Cmd, pc cli.ParsedState, cols, tabbedW int) {
	if !cc.OwnerIsNil() {
		s.bufPrintf(sb, "\n.SH %s\n", "SYNOPSIS")

		appName := cc.App().Name()
		titles := cc.GetCommandTitles()
		tail := "[tail args...]"
		if tph := cc.TailPlaceHolder(); tph != "" {
			tail = tph
		}

		s.bufPrintf(sb, ".PP\n\\fB%s\\fP %v%s%s [Options] [Parent/Global Options]"+getphtail(cc)+"\n\n",
			appName, titles, tail)
	}
	// s.Printf("\n\x1b[%dm\x1b[%dm%s\x1b[0m", bgNormal, darkColor, title)
	// fp("  [\x1b[%dm\x1b[%dm%s\x1b[0m]", bgDim, darkColor, normalize(group))
}

func (s *manPainter) printDesc(ctx context.Context, sb *strings.Builder, cc cli.Cmd, pc cli.ParsedState, cols, tabbedW int) {
	if !cc.OwnerIsNil() {
		s.bufPrintf(sb, "\n.SH %s\n", "DESCRIPTION")
		s.bufPrintf(sb, ".PP\n%v\n", cc.DescLong())
	}
}

func (s *manPainter) printExamples(ctx context.Context, sb *strings.Builder, cc cli.Cmd, pc cli.ParsedState, cols, tabbedW int) {
	if !cc.OwnerIsNil() {
		if len(cc.Examples()) > 0 && cc.OwnerIsNotNil() {
			s.bufPrintf(sb, "\n.SH %s\n", "EXAMPLES")
			s.bufPrintf(sb, ".PP\n%v\n", manExamples(cc.Examples(), cc.Root()))
		}
	}
}

func (s *manPainter) printNotes(ctx context.Context, sb *strings.Builder, cc cli.Cmd, pc cli.ParsedState, cols, tabbedW int) {
	if !cc.OwnerIsNil() {
		// s.bufPrintf(sb, "\n.SH %s\n", "DESCRIPTION")
		// s.bufPrintf(sb, ".PP\n%v\n", cc.DescLong())

		if root := cc.Root(); root.Cmd == cc && root.RedirectTo() != "" {
			// _, _ = sb.WriteString("\nNotes:\n\n")
			s.bufPrintf(sb, "\n.SH %s\n", "NOTES")
			str := exec.StripLeftTabs(fmt.Sprintf(`<i>Root Command was been redirected to Subcommand</i>: "<b>%s</b>"`, root.RedirectTo()))
			str = pc.Translate(str)
			line := s.Translate(str, color.FgDefault)
			line = exec.LeftPad(line, 2)
			_, _ = sb.WriteString(".PP\n")
			_, _ = sb.WriteString(line)
			_, _ = sb.WriteString("\n")
		}
		_, _, _ = pc, cols, tabbedW
		_ = ctx
	}
}

func (s *manPainter) printTailLine(ctx context.Context, sb *strings.Builder, cc cli.Cmd, pc cli.ParsedState, rows, cols, tabbedW int) {
	root := cc.Root()
	s.bufPrintf(sb, `
.SH SEE ALSO
.PP
\fB%v(1)\fP

.SH HISTORY
.PP
%v Auto generated by hedzr/cmdr
`, root.AppName, time.Now().Format("02-Jan-2006")) // , time.RFC822Z
}

// func (s *manPainter) printCommand(ctx context.Context, sb *strings.Builder, verboseCount *int, cc cli.Cmd, group string, idx, level, cols, tabbedW int, grouped bool) {
// 	title := "COMMANDS AND SUB-COMMANDS"
// 	s.bufPrintf(sb, "\n.SH %s\n", title)
// }

func (s *manPainter) printCommandHeading(ctx context.Context, sb *strings.Builder, cc cli.Cmd, title string) {
	title = "COMMANDS AND SUB-COMMANDS"
	s.bufPrintf(sb, "\n.SH %s\n", title)
}

func (s *manPainter) printCommandGroupTitle(ctx context.Context, sb *strings.Builder, group string, indent int) {
	// _, _ = sb.WriteString(strings.Repeat("  ", indent))
	// s.WriteColor(sb, CurrentGroupTitleColor)
	// s.WriteBgColor(sb, CurrentGroupTitleBgColor)
	// _, _ = sb.WriteString("[")
	// _, _ = sb.WriteString(group)
	// _, _ = sb.WriteString("]")
	// s.Reset(sb)
	// _, _ = sb.WriteString("\n")
	if group != cli.UnsortedGroup {
		// fp("  [%s]:", normalize(group))
		s.bufPrintf(sb, ".SS \"%s\"\n", tool.StripOrderPrefix(group))
	} else {
		s.bufPrintf(sb, ".SS \"%s\"\n", "General")
	}
}

func (s *manPainter) printCommandRow(ctx context.Context, sb *strings.Builder,
	cc cli.Cmd,
	indentSpaces, left, right, dep, depPlain string,
	cols, tabbedW int, deprecated, dim bool) {
	// if (cc.Hidden() && *verboseCount < 1) || (cc.VendorHidden() && *verboseCount < 3) { //nolint:revive
	// 	return
	// }

	// s.Printf("  %-48s%v", command.GetTitleNames(), command.Description)
	// s.Printf("\n\x1b[%dm\x1b[%dm%s\x1b[0m", bgNormal, darkColor, title)
	// s.Printf("  [\x1b[%dm\x1b[%dm%s\x1b[0m]", bgDim, darkColor, normalize(group))
	s.bufPrintf(sb, ".TP\n.BI %s", manWs(cc.GetTitleNames()))
	s.bufPrintf(sb, "\n%s\n", cc.Desc())
}

// func (s *manPainter) printFlag(ctx context.Context, sb *strings.Builder, verboseCount *int, ff *cli.Flag, group string, idx, level, cols, tabbedW int, grouped bool) {
// 	// TODO implement me
// 	panic("implement me")
// }

func (s *manPainter) printFlagHeading(ctx context.Context, sb *strings.Builder, cc cli.Cmd, ff *cli.Flag, title string) {
	// title = "COMMANDS AND SUB-COMMANDS"
	// s.bufPrintf(sb, "\n.SH %s\n", title)
	s.bufPrintf(sb, "\n.SH %s\n", "OPTIONS")
}

func (s *manPainter) printFlagGroupTitle(ctx context.Context, sb *strings.Builder, group string, indent int) {
	if group != cli.UnsortedGroup {
		s.bufPrintf(sb, ".SS \"%s\"\n", tool.StripOrderPrefix(group))
	} else {
		s.bufPrintf(sb, ".SS \"%s\"\n", "General")
	}
}

func (s *manPainter) printFlagRow(ctx context.Context, sb *strings.Builder,
	ff *cli.Flag,
	indentSpaces, left, right, tg, def, defPlain, dep, depPlain, env, envPlain string,
	cols, tabbedW int, deprecated, dim bool) {
	// s.Printf(".TP\n.BI %s\n%s\n%s\n", manWs(flag.GetTitleFlagNames()), flag.Description, defValStr)
	title, rest := ff.GetTitleFlagNames()
	s.bufPrintf(sb, ".TP\n.BI %s", manWs("%s %s", title, rest))
	s.bufPrintf(sb, "\n%s\n%s\n", ff.DescLong(), defPlain)
}

var _ Painter = (*manPainter)(nil)

func newManPainter() *manPainter {
	return &manPainter{
		writer:     new(bytes.Buffer),
		Translator: color.GetCPT(),
		// buffer:bufio.NewWriterSize(),
	}
}

func (s *manPainter) Results() (res []byte) {
	if bb, ok := s.writer.(*bytes.Buffer); ok {
		res = bb.Bytes()
	}
	return
}

func (s *manPainter) Reset() {
	s.writer = nil
	s.writer = new(bytes.Buffer)
}

func (s *manPainter) Flush() {
	// if bb, ok := s.writer.(*bytes.Buffer); ok {
	// 	_, _ = fmt.Fprintf(os.Stdout, "%v\n", bb.String())
	// }
}

func (s *manPainter) wrPrint(wr io.Writer, fmtStr string, args ...interface{}) {
	str := fmt.Sprintf(fmtStr, args...)
	str = replaceAll(str, "-", `\-`)
	str = replaceAll(str, "`cmdr`", `\fBcmdr\fP`)
	_, _ = wr.Write([]byte(s.Translate(str, color.Reset)))
}

func (s *manPainter) bufPrintf(buf *strings.Builder, fmtStr string, args ...interface{}) {
	str := fmt.Sprintf(fmtStr, args...)
	str = replaceAll(str, "-", `\-`)
	str = replaceAll(str, "`cmdr`", `\fBcmdr\fP`)
	_, _ = buf.WriteString(s.Translate(str, color.Reset))
}

func replaceAll(s, old, newstr string) string {
	return strings.ReplaceAll(s, old, newstr)
}

func tplApply(tmpl string, data interface{}) string {
	w := new(bytes.Buffer)
	tpl := template.Must(template.New("x").Parse(tmpl))
	if err := tpl.Execute(w, data); err != nil {
		logz.Printf("tpl execute error: %v", err)
		return ""
	}
	return w.String()
}

func manBr(s string) string {
	var lines []string //nolint:prealloc //can't prealloc
	for _, l := range strings.Split(s, "\n") {
		lines = append(lines, l+"\n.br")
	}
	return strings.Join(lines, "\n")
}

func manWs(fmtStr string, args ...interface{}) string {
	str := fmt.Sprintf(fmtStr, args...)
	str = replaceAll(strings.TrimSpace(str), " ", `\ `)
	return str
}

func manExamples(s string, data interface{}) string {
	var (
		sources  = strings.Split(s, "\n")
		lines    []string
		lastLine string
	)
	for _, l := range sources {
		if strings.HasPrefix(l, "$ {{.AppName}}") {
			lines = append(lines, `.TP \w'{{.AppName}}\ 'u
.BI {{.AppName}} \ `+manWs(l[14:]))
		} else {
			if lastLine == "" {
				lastLine = strings.TrimSpace(l)
				// ignore multiple empty lines, compat them as one line.
				if lastLine != "" {
					lines = append(lines, lastLine+"\n.br")
				}
			} else {
				lastLine = strings.TrimSpace(l)
				lines = append(lines, lastLine+"\n.br")
			}
		}
	}
	return tplApply(strings.Join(lines, "\n"), data)
}

type manHdrData struct {
	*cli.RootCommand
	LongDesc    string
	TimeMY      string
	ManExamples string
}
